#  StormMUD - A multiplayer combat game - http://stormmud.david-c-brown.com
#  Copyright (C) 2009, 2010 - David C Brown & Mark Richardson
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.

from smDefines import GREEN, WHITE, LCYAN, MAGENTA, DIRS, MAXITEMS
from smConfig import Config
from smLog import smLogger, logger, WARN, DEBUG
from smutils import FindItemName

import smPlayer

from twisted.internet import reactor

import random

# WORLD is a hash of all maps the game consist of.  "MAPID: MapObject"
WORLD = {}
PATHWAY = 0
#===============================================
# Map Class
#===============================================
class Map:
    """Map object definition"""

    #===========================================
    # __init__()
    #===========================================
    def __init__(self):
        """Map -> __init__(): Initialize new Map object"""

        self.MapID           = ""
        self.creator         = ""
        self.name            = ""
        self.desc            = ""
        self.rooms           = {}
        self.doors           = {}
        self.items           = {}
        self.spells          = {}
        self.traps           = {}
        self.NPCList         = {}


#================================================
# Rooms Class
#================================================
class Rooms:
    """Room object definition"""

    #============================================
    # __init__()
    #============================================
    def __init__(self):
        """Rooms -> __init__(): Initialize new room object"""

        self.RoomID                   = ""
        self.RoomType                 = []
        self.name                     = ""
        self.desc1                    = ""
        self.desc2                    = ""
        self.desc3                    = ""
        self.desc4                    = ""
        self.desc5                    = ""
        self.LightLevel               = 1
        self.doors                    = {}
        self.triggers                 = {}
        self.PlayersInRoom            = {}
        self.ItemsOnGround            = {}
        self.HiddenItems              = {}
        self.npcs                     = []

    #============================================
    # Rooms -> GetObviousExits()
    #============================================
    def GetObviousExits(self):
        """
        GetObviousExits():
        Return a string of obvious exits in the current room
        """

        DoorCount = 0
        ObviousExits = "%sObvious exits:" % (GREEN)

        for direction in self.doors.keys():
            CurDoor = self.doors[direction]

            if not CurDoor.hidden and CurDoor.DoorType == PATHWAY:
                DoorCount += 1
                if DoorCount > 1:
                    ObviousExits += ", %s" % (DIRS[direction])
                else:
                    ObviousExits += " %s" % (DIRS[direction])
            elif not CurDoor.hidden:
                DoorCount += 1
                if CurDoor.IsOpen:
                    doorstatus = "open"
                else:
                    doorstatus = "closed"
                #
                # TODO: Added the Doors description text here (open gate, closed gate, etc)
                #
                if DoorCount > 1:
                    ObviousExits += ", %s %s %s" % (doorstatus, CurDoor.DoorText, DIRS[direction])
                else:
                    ObviousExits += " %s %s %s" % (doorstatus, CurDoor.DoorText, DIRS[direction])

        # Return NONE, or add the finally period to close the sentence
        if DoorCount == 0:
            ObviousExits += "NONE!"
        else:
            ObviousExits += "."

        return ObviousExits
    
    #============================================
    # Rooms -> GetAlsoHere()
    #============================================
    def GetAlsoHere(self, playerName):
        count = 0
        line = ""
        for user in self.PlayersInRoom.keys():
            if user != playerName:
                if count > 0:
                    count += 1
                    line += ", %s" % (user)
                else:
                    count += 1
                    line = user
        
        return "%sAlso here:%s %s" % (GREEN, MAGENTA, line)
    #============================================
    # Rooms -> ItemsYouNotice()
    #============================================
    def ItemsYouNotice(self, Hidden):
        """
        ItemsYouNotice(self, Hidden):
        Return a string of items noticed in the current room.  If Hidden = True,
        Return text of items hidden in the room rather than not hidden
        """
        # Are we noticing items on ground or hidden items?
        if Hidden:
            NumItems = len(self.HiddenItems)
            Items = self.HiddenItems
        else:
            NumItems = len(self.ItemsOnGround)
            Items = self.ItemsOnGround

        if NumItems == 0:
            return None
        else:
            itemStr = ""
            x = 1
            # Cycle through the items
            for itemName in Items.keys():
                if x == NumItems and x == 1:
                    # If there are more than one, list quanity along with items name
                    if Items[itemName].count > 1:
                        itemStr += "%i %s" % (Items[itemName].count, itemName)
                    else:
                        itemStr += itemName
                elif x == itemsName:
                    if Items[itemName].count > 1:
                        itemStr += ", %i %s" % (Items[itemName].count, itemName)
                    else:
                        itemStr += itemName
                    itemStr += ", %s" % (itemName)

        return itemStr

    #============================================
    # Rooms -> FindItemInRoom()
    #============================================
    def FindItemInRoom(self, SearchText, FoundHidden):
        """
        FindItemInRoom(SearchText)
        Checks the rooms items to see if something matches. If several
        things match, return a list of items.
        """

        SearchList = {}
        # Are we searching hidden and non-hidden items?
        if FoundHidden:
            SearchList.update(self.HiddenItems)

        SearchList.update(ItemsOnGround)
        NameSearch = re.compile( re.escape(Name.lower()) )
        for itemName, item in SearchList.items():
            if itemName != "":
                if NameSearch.match( itemName.lower() ):
                    itemList[itemName] = item

        if len(itemList) == 0:
            return None
        else:
            return itemList

    #============================================
    # Rooms -> FindUserInRoom()
    #============================================
    def FindUserInRoom(self, SearchText):
        """
        FindUserInRoom(SearchText, FoundHidden)
        Checks the room to see if a user in the room matches
        SearchText.  Returns a list of all users that match
        or an empty list if no users match.
        """

        NameSearch = re.compile( re.escape(Name.lower()) )
        return [name for name in self.PlayersInRoom.keys() if NameSearch.match(name.lower())]

    
    #============================================
    # Rooms -> RemovePlayerFromRoom()
    #============================================
    def RemovePlayerFromRoom(self, player):
        """
        Rooms->RemovePlayerFromRoom(player)
        Remove a player from current room
        """
        
        try:
            del self.PlayersInRoom[player.name]
            player.room = None
        except IndexError:
            logger.Logit(WARN, "IndexError: %s is not in room: %s" % (player.name, self.RoomID) )
            
        
    #============================================
    # Rooms -> AddPlayerToRoom()
    #============================================
    def AddPlayerToRoom(self, player):
        """
        Rooms->AddPlayerToRoom(player)
        Adds the player to the current room
        """
        
        if player.name in self.PlayersInRoom:
            logger.Logit( WARN, "Error: Player %s is already in the room: %s" % (player.name, self.RoomID) )
        else:
            self.PlayersInRoom[player.name] = player
            player.room = self.RoomID
            
            
    #============================================
    # Rooms -> AddItemToRoom()
    #============================================
    def AddItemToRoom(self, item, quantity):    
        """
        Rooms->AddItemToRoom(item, quantity)
        Add item or quantity of a single item to
        the room
        """
        if self.ItemsOnGround.has_key(item.name):
            self.ItemsOnGround[item.name].quantity += quantity
            return True
        else:
            if len(self.ItemsOnGround) > MAXITEMS:
                logger.Logit( DEBUG, "To many items in room %s, not adding %s." % (self.RoomID, item.name) )
                return False
            else:
                self.ItemsOnGround[item.name] = item
                return True
                                    
                
#================================================
# Doors Class
#================================================
class Doors:
    """Room object definition"""

    #============================================
    # __init__()
    #============================================
    def __init__(self):
        """Doors -> __init__(): Initialize new door object"""

        self.DoorID                   = ""
        self.DoorType                 = 0
        self.DoorText                 = ""
        self.passable                 = False
        self.IsOpen                   = False
        self.operable                 = True
        self.transparent              = False
        self.hidden                   = False
        self.locked                   = False
        self.IsLocked                 = False
        self.LockDifficulty           = 0
        self.BashDifficulty           = 0
        self.trap                     = None
        self.IsTrapped                = None
        self.ExitRoom                 = {}

    #=============================================
    # UnlockDoor()
    #=============================================
    def UnlockDoor(self, player):
        """
        UnlockDoor()
        Function unlocks the door. (self)
        """

        self.IsLocked = False
        if self.locked == True:
            reactor.callLater( 15, self.LockDoor )


    #=============================================
    # LockDoor()
    #=============================================
    def LockDoor(self):
        """
        LockDoor()
        Locks the door and tells the room the door locked
        """

        if self.IsOpen:
            self.IsOpen == False

        self.IsLocked = True
        #
        # TODO: Tell room the door locked
        #

    #==============================================
    # Door->BashDoor()
    #==============================================
    def BashDoor(self, player):
        """
        BashDoor()
        Rolls to determine if player successfuly bashes
        the door open
        """
        def BashFail():
            # TODO: send failed bash messages
            pass
        def BashSuccess():
            # TODO: send sucessful bash message and open door
            pass

        # If 1000, automatic fail
        if self.BashDifficulty == 1000:
            BashFail()
            return None

        BashPower = player.strength if player.wearing[WIELDED] else player.strength + int(player.wearing[WIELDED].weight / 2)
        if BashPower > self.BashDifficulty:
            if (BashPower - self.BashDifficulty) > 100:
                BashSuccess()
                return
            else:
                BashSucess() if random.randint(1, 100) > (BashPower - self.BashDifficulty) else BashFail()

    #================================================
    # Doors->PickLock()
    #================================================
    def PickLock(self, player):
        """
        Doors->PickLock()
        Rolles to determine if the player successfully picks
        the lock.
        """
        def PickFail():
            # TODO: send failed pick lock fail messages
            pass
        def PickSuccess():
            # TODO: send sucessful pick message
            self.UnlockDoor()
            player.sendToPlayer( "%sYou pick the lock on the %s" % (WHITE, self.name) )
            player.sendToRoom( "%sYou notice %s pick the lock on the %s" % (WHITE, player.name, self.name) )

        if (self.LockDifficulty == 1000 or player.picklocks == 0):
            PickFail()
        elif (self.picklocks - self.LockDifficulty > 99):
            PickSuccess()
        else:
            PickSucess() if random.randint(1, 100) > (player.picklocks - self.LockDifficulty) else PickFail()

#=====================================================
# GetMapIDFromRoomID()
#=====================================================
def GetMapIDFromRoomID(RoomID):
    """
    GetMapIDFromRoomID()
    Returns the MapID from a given RoomID.
    """
    
    try:
        MapID = RoomID.split("|")[0]
    except:
        logger.Logit(ERROR, "smMaps.GetMapIDFromRoomID() recieved an invalid Room ID: %s" % (RoomID) )
        # At some point make return to a default room if fail?!?!
        return None
    
    return MapID

#======================================================
# GetRoomByID()
#======================================================
def GetRoomByID(RoomID):
    """
    GetRoomByID()
    Returns a room object by it's ID
    """
    
    MapID = GetMapIDFromRoomID(RoomID)
    try:
        Room = WORLD[MapID].rooms[RoomID]
    except:
        logger.Logit(ERROR, "smMaps.GetRoomByID(): Room does not exist: %s" % (RoomID) )
        return None
    
    return Room
    